//! System call error handling
//! SPDX-License-Identifier: MPL-2.0

const std = @import("std");

/// Linux-compatible error codes
pub const SyscallError = enum(i32) {
    // Success
    SUCCESS = 0,
    
    // Common errors
    EPERM = 1,      // Operation not permitted
    ENOENT = 2,     // No such file or directory
    ESRCH = 3,      // No such process
    EINTR = 4,      // Interrupted system call
    EIO = 5,        // I/O error
    ENXIO = 6,      // No such device or address
    E2BIG = 7,      // Argument list too long
    ENOEXEC = 8,    // Exec format error
    EBADF = 9,      // Bad file number
    ECHILD = 10,    // No child processes
    EAGAIN = 11,    // Try again
    ENOMEM = 12,    // Out of memory
    EACCES = 13,    // Permission denied
    EFAULT = 14,    // Bad address
    ENOTBLK = 15,   // Block device required
    EBUSY = 16,     // Device or resource busy
    EEXIST = 17,    // File exists
    EXDEV = 18,     // Cross-device link
    ENODEV = 19,    // No such device
    ENOTDIR = 20,   // Not a directory
    EISDIR = 21,    // Is a directory
    EINVAL = 22,    // Invalid argument
    ENFILE = 23,    // File table overflow
    EMFILE = 24,    // Too many open files
    ENOTTY = 25,    // Not a typewriter
    ETXTBSY = 26,   // Text file busy
    EFBIG = 27,     // File too large
    ENOSPC = 28,    // No space left on device
    ESPIPE = 29,    // Illegal seek
    EROFS = 30,     // Read-only file system
    EMLINK = 31,    // Too many links
    EPIPE = 32,     // Broken pipe
    EDOM = 33,      // Math argument out of domain of func
    ERANGE = 34,    // Math result not representable
    EDEADLK = 35,   // Resource deadlock would occur
    ENAMETOOLONG = 36, // File name too long
    ENOLCK = 37,    // No record locks available
    ENOSYS = 38,    // Invalid system call number
    ENOTEMPTY = 39, // Directory not empty
    ELOOP = 40,     // Too many symbolic links encountered
    EWOULDBLOCK = 11, // Would block (same as EAGAIN)
    ENOTSOCK = 88,  // Socket operation on non-socket
    EDESTADDRREQ = 89, // Destination address required
    EMSGSIZE = 90,  // Message too long
    EPROTOTYPE = 91, // Protocol wrong type for socket
    ENOPROTOOPT = 92, // Protocol not available
    EPROTONOSUPPORT = 93, // Protocol not supported
    ESOCKTNOSUPPORT = 94, // Socket type not supported
    EOPNOTSUPP = 95, // Operation not supported on transport endpoint
    EPFNOSUPPORT = 96, // Protocol family not supported
    EAFNOSUPPORT = 97, // Address family not supported by protocol
    EADDRINUSE = 98, // Address already in use
    EADDRNOTAVAIL = 99, // Cannot assign requested address
    ENETDOWN = 100, // Network is down
    ENETUNREACH = 101, // Network is unreachable
    ENETRESET = 102, // Network dropped connection because of reset
    ECONNABORTED = 103, // Software caused connection abort
    ECONNRESET = 104, // Connection reset by peer
    ENOBUFS = 105,  // No buffer space available
    EISCONN = 106,  // Transport endpoint is already connected
    ENOTCONN = 107, // Transport endpoint is not connected
    ESHUTDOWN = 108, // Cannot send after transport endpoint shutdown
    ETOOMANYREFS = 109, // Too many references: cannot splice
    ETIMEDOUT = 110, // Connection timed out
    ECONNREFUSED = 111, // Connection refused
    EHOSTDOWN = 112, // Host is down
    EHOSTUNREACH = 113, // No route to host
    EALREADY = 114, // Operation already in progress
    EINPROGRESS = 115, // Operation now in progress
    ESTALE = 116,   // Stale file handle
    ERESTARTSYS = 512,
    ERESTARTNOINTR = 513,
    ERESTARTNOHAND = 514,
    ERESTART_RESTARTBLOCK = 516,
    
    _,
    
    /// Convert to negative return value (Linux convention)
    pub fn to_return_value(self: SyscallError) i64 {
        return -@as(i64, @intFromEnum(self));
    }
    
    /// Check if this is a restartable error
    pub fn is_restartable(self: SyscallError) bool {
        return switch (self) {
            .ERESTARTSYS,
            .ERESTARTNOINTR,
            .ERESTARTNOHAND,
            .ERESTART_RESTARTBLOCK => true,
            else => false,
        };
    }
    
    /// Get error description
    pub fn description(self: SyscallError) []const u8 {
        return switch (self) {
            .SUCCESS => "Success",
            .EPERM => "Operation not permitted",
            .ENOENT => "No such file or directory",
            .ESRCH => "No such process",
            .EINTR => "Interrupted system call",
            .EIO => "I/O error",
            .ENXIO => "No such device or address",
            .E2BIG => "Argument list too long",
            .ENOEXEC => "Exec format error",
            .EBADF => "Bad file number",
            .ECHILD => "No child processes",
            .EAGAIN => "Try again",
            .ENOMEM => "Out of memory",
            .EACCES => "Permission denied",
            .EFAULT => "Bad address",
            .ENOTBLK => "Block device required",
            .EBUSY => "Device or resource busy",
            .EEXIST => "File exists",
            .EXDEV => "Cross-device link",
            .ENODEV => "No such device",
            .ENOTDIR => "Not a directory",
            .EISDIR => "Is a directory",
            .EINVAL => "Invalid argument",
            .ENFILE => "File table overflow",
            .EMFILE => "Too many open files",
            .ENOTTY => "Not a typewriter",
            .ETXTBSY => "Text file busy",
            .EFBIG => "File too large",
            .ENOSPC => "No space left on device",
            .ESPIPE => "Illegal seek",
            .EROFS => "Read-only file system",
            .EMLINK => "Too many links",
            .EPIPE => "Broken pipe",
            .EDOM => "Math argument out of domain of func",
            .ERANGE => "Math result not representable",
            .EDEADLK => "Resource deadlock would occur",
            .ENAMETOOLONG => "File name too long",
            .ENOLCK => "No record locks available",
            .ENOSYS => "Invalid system call number",
            .ENOTEMPTY => "Directory not empty",
            .ELOOP => "Too many symbolic links encountered",
            .ENOTSOCK => "Socket operation on non-socket",
            .EDESTADDRREQ => "Destination address required",
            .EMSGSIZE => "Message too long",
            .EPROTOTYPE => "Protocol wrong type for socket",
            .ENOPROTOOPT => "Protocol not available",
            .EPROTONOSUPPORT => "Protocol not supported",
            .ESOCKTNOSUPPORT => "Socket type not supported",
            .EOPNOTSUPP => "Operation not supported on transport endpoint",
            .EPFNOSUPPORT => "Protocol family not supported",
            .EAFNOSUPPORT => "Address family not supported by protocol",
            .EADDRINUSE => "Address already in use",
            .EADDRNOTAVAIL => "Cannot assign requested address",
            .ENETDOWN => "Network is down",
            .ENETUNREACH => "Network is unreachable",
            .ENETRESET => "Network dropped connection because of reset",
            .ECONNABORTED => "Software caused connection abort",
            .ECONNRESET => "Connection reset by peer",
            .ENOBUFS => "No buffer space available",
            .EISCONN => "Transport endpoint is already connected",
            .ENOTCONN => "Transport endpoint is not connected",
            .ESHUTDOWN => "Cannot send after transport endpoint shutdown",
            .ETOOMANYREFS => "Too many references: cannot splice",
            .ETIMEDOUT => "Connection timed out",
            .ECONNREFUSED => "Connection refused",
            .EHOSTDOWN => "Host is down",
            .EHOSTUNREACH => "No route to host",
            .EALREADY => "Operation already in progress",
            .EINPROGRESS => "Operation now in progress",
            .ESTALE => "Stale file handle",
            .ERESTARTSYS => "Restart system call",
            else => "Unknown error",
        };
    }
};

/// Result type for system calls
pub const SyscallResult = union(enum) {
    success: u64,
    error: SyscallError,
    
    pub fn ok(value: u64) SyscallResult {
        return SyscallResult{ .success = value };
    }
    
    pub fn err(error_code: SyscallError) SyscallResult {
        return SyscallResult{ .error = error_code };
    }
    
    pub fn is_error(self: SyscallResult) bool {
        return switch (self) {
            .success => false,
            .error => true,
        };
    }
    
    pub fn unwrap(self: SyscallResult) u64 {
        return switch (self) {
            .success => |val| val,
            .error => |err| @panic(err.description()),
        };
    }
    
    pub fn unwrap_or(self: SyscallResult, default: u64) u64 {
        return switch (self) {
            .success => |val| val,
            .error => default,
        };
    }
};